#ifdef ENABLE_MAPLE_SAN

#ifndef MAPLE_SAN_ASAN_RAZOR_H
#define MAPLE_SAN_ASAN_RAZOR_H

#include "asan_config.h"
#include "asan_function.h"
#include "asan_mapping.h"
#include "asan_module.h"
#include "me_cfg.h"
#include "me_function.h"
#include "me_ssa.h"
#include "mir_module.h"
#include "mpl_logging.h"
#include "san_common.h"

#include "asan_ud.h"

namespace maple {

typedef float AsanBlockCost;

class ASanRazorStmtKey;
extern std::string SanRazorCounterName;
extern std::string SanRazorSizeName;
extern std::string SanRazorIsInitializedName;
extern std::string SanRazorFilenameName;
extern std::atomic<size_t> SanRazorSize;
extern std::string SanRazorCheckIDMapFileName;
extern bool SanRazorIs2ndCompile;
// store from CallSiteID to AsanRazorCheckID
extern std::map<size_t, AsanRazorCheckID> SanRazorCheckIDMap;
extern std::map<const ASanRazorStmtKey, AsanRazorCheckID> SanRazorStmtKeyCheckIDMap;
extern std::map<const ASanRazorStmtKey, AsanBlockCost> SanRazorStmtKey2CostMap;
extern std::set<AsanRazorCheckID> AsanRelevanCheckIDs;
extern std::set<ASanRazorStmtKey> SanRazorAsanRelevantStmtKey;
void InitializeSanRazorGlobalNames(ModuleAddressSanitizer& AsanModule);
void SetSanRazorGlobals(ModuleAddressSanitizer& AsanModule);
std::string GetSanRazorCheckIDMapDirPath();
void DumpSanRazorCheckIDMap(std::string filename);
void AddASanRazorSignalHandlerRegister(ModuleAddressSanitizer& AsanModule);
void AddSanRazorAppendInfo(ModuleAddressSanitizer& AsanModule);
void LoadSanRazorCheckIDMap(std::string filename);
void LoadSanRazorStmtKeyCheckIDMap(std::string filename);
extern std::string SanRazorLineDelimiter;
extern std::map<const ASanRazorStmtKey, AsanRazorCheckCount> SanRazorStmtCountMap;
void LoadSanRazorLog();
void LoadAllSanRazorStmtKeyCheckIDMaps();
void SanRazorClearEverything();

class ASanRazorStmtKey {
public:
  size_t callSiteId;
  std::string src_path;

  ASanRazorStmtKey(){};
  ASanRazorStmtKey(size_t callSiteId, std::string src_path) : callSiteId(callSiteId), src_path(src_path){};

  bool operator<(const ASanRazorStmtKey &rhs) const {
    if (this->callSiteId == rhs.callSiteId) {
      return this->src_path < rhs.src_path;
    }
    return this->callSiteId < rhs.callSiteId;
  }

  bool operator>(const ASanRazorStmtKey &rhs) const {
    if (this->callSiteId == rhs.callSiteId) {
      return this->src_path > rhs.src_path;
    }
    return this->callSiteId > rhs.callSiteId;
  }

  bool operator==(const ASanRazorStmtKey &rhs) const {
    if (this->callSiteId == rhs.callSiteId) {
      return this->src_path == rhs.src_path;
    }
    return false;
  }

  void SetCallSiteID(const size_t callSiteId) {
    this->callSiteId = callSiteId;
  }

  void SetSrcPath(const std::string src_path) {
    this->src_path = src_path;
  }

  StmtID GetStmtID() const {
    AsanRazorCheckID id = SanRazorCheckIDMap[this->callSiteId];
    return id >> 1;
  }

  bool IsTrueBranch() const {
    AsanRazorCheckID id = SanRazorCheckIDMap[this->callSiteId];
    return (id & 0x1) > 0;
  }
};

class ASanRazor {
 public:
  ASanRazor();

  bool InstrumentFunctionForProfiling(MeFunction &F, const AsanVirtualBlockList &AsanBlocks);
  bool RemoveRedundantInstrumentation(MeFunction &F, const AsanVirtualBlockList &AsanBlocks);
  void ClearSanRazorCoverageStmtNodes();

 private:
  MIRModule *module;
  MeFunction *mefunc;
  MIRFunction *mirfunc;
  std::string srcpath;
  UStrIdx srcpathidx;
  std::map<StmtID, StmtNode*> stmtID2node;
  std::set<StmtID> asanStmtIDSet;
  std::map<LabelIdx, LabelNode*> labelIdx2LabelNode;
  std::map<LabelIdx, BlockNode*> labelIdx2BlockNode;
  std::map<StmtID, const AsanVirtualBlock*> stmtID2asanBlock;
  std::vector<StmtBlockNodePair> asanCovStmts; // if SanRazorIs2ndCompile, remove them all
  std::map<StmtID, BlockNode*> stmtID2BlockNode;

  void SetFunc(MeFunction &F) {
    this->module = &(F.GetMIRModule());
    this->mefunc = &F;
    this->mirfunc = F.GetMirFunc();
    this->srcpath = AsanModulePtr->GetSrcFilePath();
    this->srcpathidx = GlobalTables::GetUStrTable().GetOrCreateStrIdxFromName(this->srcpath);
  }

  void _GetLabelIdx2LabelNodeFromBlock(BlockNode *block);
  std::map<LabelIdx, LabelNode*> GetLabelIdx2LabelNode();
  std::map<LabelIdx, BlockNode*> GetLabelIdx2BlockNode();
  void _GetStmtID2node(BlockNode* block);
  std::map<StmtID, StmtNode*> GetStmtID2node();
  AsanVirtualBlock* GetAsanVirtualBlockWithIDBlock(const AsanVirtualIDBlock &asanBlock);
  std::set<StmtID> GetAsanStmtIDSet(const AsanVirtualBlockList &AsanBlocks);
  void PreprocessFunctionForProfiling(MeFunction &F, const AsanVirtualBlockList &AsanBlocks) {
    this->SetFunc(F);
    this->GetStmtID2node();
    this->GetAsanStmtIDSet(AsanBlocks);
    this->GetLabelIdx2LabelNode();
  }

  CallNode *GetCallAsanCovStmt(AsanRazorCheckID stmt_id);
  CallNode* GetCallDumpAsanCovStmt();
  AsanRazorCheckID GetTrueBranchID(StmtNode* stmt) {
    return (stmt->GetStmtID() << 1) | 0x1;
  }
  AsanRazorCheckID GetFalseBranchID(StmtNode* stmt) {
    return (stmt->GetStmtID() << 1);
  }
  void InstrumentAsanCheckForProfiling(StmtNode *stmt, const AsanVirtualBlock &asanBlock, BlockNode *block);
  void InstrumentIfStmtForProfiling(IfStmtNode* ifStmt);
  void InstrumentWhileStmtForProfiling(WhileStmtNode* whileStmt, BlockNode* block);
  void InstrumentDoloopForProfiling(DoloopNode* doloop, BlockNode* block);
  void InstrumentCondGotoForProfiling(CondGotoNode* condGoto, BlockNode* block, bool isTrue);
  void InstrumentForeachelemForProfiling(ForeachelemNode* foreachelem, BlockNode* block);
  bool InstrumentBlockForProfiling(BlockNode *block);


  UDProfile udchain; 
  std::set<StmtID> UserCheckStmtID;
  std::set<StmtID> SanCheckStmtID;
  std::set<StmtID> GetRemoveAsanID();
  san_struct Get_san_struct(StmtID id);



};

}  // namespace maple

#endif  // MAPLE_SAN_ASAN_RAZOR_H

#endif  // ENABLE_MAPLE_SAN